package org.yray.sip.server;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import io.netty.util.Timeout;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import org.springframework.util.StopWatch;
import org.yray.sip.base.MsgEle;
import org.yray.sip.base.SipMsg;
import org.yray.sip.constant.SessionStatusEnum;
import org.yray.sip.utils.IdUtils;

import java.io.Serializable;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

@Data
public class SipSession implements Serializable {


    //开始时间，发起外呼的时间
    private long beginTime;
    public static final  String BRANCH_PRE="z9hG4bK";
    public static final  String UNKOWN="Unkown";
    private String branch;
    private String tag;
    private String sipIp;
    private int sipPort;
    private String localIp;
    private int localPort;
    private String ext;
    private String from;
    private String to;
    private String authorization;
    private Map<String, String> sipMap;
    //自己的ssrc
    private Long ssrc;
    private String cname;
    private int ptime;
    private String mediaFormat;
    private int rtpSeq;
    //第一个RR包的时间，即应答的时间
    private long sendRRTime;

    private String nonce;
    private String realm;
    private Timeout timeout;

    private transient List<String> sipList;

    @Getter(AccessLevel.NONE)
    private InetSocketAddress sideRtpSender;

    private InetSocketAddress otherRtpSender;
    //另一端的ssrc
    private Long sideSsrc;
    private String fileName;

    private transient StopWatch stopWatch;
    //转人工的列表
    private String[]toManExt;
    private int toManIndex;
    private long asrTime;
    //表明这个就是转人工的
    private volatile boolean stopAsr=false;
    private AtomicReference<SessionStatusEnum>  status=new AtomicReference<>(SessionStatusEnum.CALL_ING);
    private String sourceId;
    private volatile String toManId;
    private volatile String toManWaitId;
    private AtomicInteger sentenceSn=new AtomicInteger(0);
    private Integer  asrIndex;
    private AtomicBoolean bye=new AtomicBoolean(false);

    public long getAsrTime() {
        return asrTime;
    }

    public void setAsrTime(long asrTime) {
        this.asrTime = asrTime;
    }

    public int incSentenceSnAndGet()
    {
       return sentenceSn.incrementAndGet();
    }
    public final static int PCMA_STEP_SIZE=160;
    public final static int PCMA_STEP_TIME_LEN=20;
    public final static int PCMA_STEP_STAMP=160;
    public int getPtime() {
        return ptime;
    }



    public void setPtime(int ptime) {
        this.ptime = ptime;
    }

    public String getMediaFormat() {
        return mediaFormat;
    }

    public void setMediaFormat(String mediaFormat) {
        this.mediaFormat = mediaFormat;
    }

    private String sideRtcpIp;
    private int sideRtcpPort;
    private int sideRtpPort;

    public String getSideRtcpIp() {
        return sideRtcpIp;
    }

    public void setSideRtcpIp(String sideRtcpIp) {
        this.sideRtcpIp = sideRtcpIp;
    }

    public int getSideRtcpPort() {
        return sideRtcpPort;
    }

    public void setSideRtcpPort(int sideRtcpPort) {
        this.sideRtcpPort = sideRtcpPort;
    }

    public int getSideRtpPort() {
        return sideRtpPort;
    }

    public void setSideRtpPort(int sideRtpPort) {
        this.sideRtpPort = sideRtpPort;
    }


    public String getAuthorization() {
        return authorization;
    }

    public void setAuthorization(String authorization) {
        this.authorization = authorization;
    }

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }

    private int cseq;

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    private String callId;

    public String getExt() {
        return ext;
    }

    public void setExt(String ext) {
        this.ext = ext;
    }

    public SipSession(String ext,String sipIp,int sipPort,String runIp,int runPort)
    {
        String tempCallId= UUID.randomUUID().toString().replace("-","");
        createSipSession(tempCallId, ext, sipIp, sipPort, runIp, runPort);
    }

    public SipSession(String callId,String ext,String sipIp,int sipPort,String runIp,int runPort)
    {
        createSipSession(callId, ext, sipIp, sipPort, runIp, runPort);
    }

    public void createSipSession(String callId,String ext,String sipIp,int sipPort,String runIp,int runPort)
    {
        this.callId= callId;
        this.branch=BRANCH_PRE+ SecureUtil.md5(callId+"branch");
        this.tag=SecureUtil.md5(callId+"tag");
        this.sipIp=sipIp;
        this.sipPort=sipPort;
        this.localIp=runIp;
        this.localPort=runPort;
        this.cseq=5201314;
        this.ext=ext;
        this.ssrc= IdUtils.generateSsrc(new Object());
        this.cname= IdUtils.generateCname(new Object());
        this.stopWatch=new StopWatch(callId);
        this.sipList=new ArrayList<>();
    }

    public SipSession (SipMsg sipMsg, String runIp, int runPort, boolean newBranch)
    {
        this.callId=Optional.ofNullable(sipMsg.getMsgLine(MsgEle.CALL_ID)).map(String::trim).orElse(null);
        this.branch=newBranch?(BRANCH_PRE+ SecureUtil.md5(callId+"branch"+ RandomUtil.randomNumber())):sipMsg.getBranch();
        this.tag=newBranch?SecureUtil.md5(callId+"tag"+ RandomUtil.randomNumber()):sipMsg.getTag();
        this.sipIp=sipMsg.getReceiveIp();
        this.sipPort=sipMsg.getReceivePort();
        this.localIp=runIp;
        this.localPort=runPort;
        this.cseq=5201314;
        this.ext=Optional.ofNullable(sipMsg.getMsgLine(MsgEle.CONTACT)).map(e-> e.substring(e.indexOf(':')+1,e.lastIndexOf('@'))).orElse(null);
        this.from=Optional.ofNullable(sipMsg.getMsgLine(MsgEle.FROM)).map(e-> e.substring(e.indexOf(':')+1,e.lastIndexOf('@'))).orElse(null);
        this.to=Optional.ofNullable(sipMsg.getMsgLine(MsgEle.TO)).map(e-> e.substring(e.indexOf(':')+1,e.lastIndexOf('@'))).orElse(null);
        this.sipMap=sipMsg.getSipMap();
        this.stopWatch=new StopWatch(callId);
        this.sipList=new ArrayList<>();
        this.ssrc= IdUtils.generateSsrc(new Object());
        this.cname= IdUtils.generateCname(new Object());

    }

    public static SipSession createRegisterSession(SipMsg sipMsg, String runIp, int runPort)
    {
        SipSession sipSession=new SipSession(sipMsg,runIp,runPort,false);
        sipSession.setRealm(runIp);
        sipSession.setNonce(UUID.randomUUID().toString());
        return sipSession;
    }

    public String getLocalIpPort()
    {
        return localIp+":"+localPort;
    }
    public String getSipIpPort()
    {
        return sipIp+":"+sipPort;
    }
    public String getCallId() {
        return callId;
    }

    public String getBranch() {
        return branch;
    }

    public String getTag() {
        return tag;
    }

    public void setCallId(String callId) {
        this.callId = callId;
    }

    public void setBranch(String branch) {
        this.branch = branch;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public String getSipIp() {
        return sipIp;
    }

    public void setSipIp(String sipIp) {
        this.sipIp = sipIp;
    }

    public int getSipPort() {
        return sipPort;
    }

    public void setSipPort(int sipPort) {
        this.sipPort = sipPort;
    }

    public String getLocalIp() {
        return localIp;
    }

    public void setLocalIp(String localIp) {
        this.localIp = localIp;
    }

    public int getLocalPort() {
        return localPort;
    }

    public void setLocalPort(int localPort) {
        this.localPort = localPort;
    }
    //<sip:78004@172.168.200.78>
    public String getSipExtIp()
    {
        return "<sip:"+ext+"@"+sipIp+">";
    }

    //"78004" <sip:78004@172.168.200.78>
    public String getSipExtIpName()
    {
        return "\""+ext+"\""+getSipExtIp();
    }
    public String getToExt()
    {
        return "<sip:"+to+"@"+sipIp+">";
    }

    //Contact: "78004" <sip:78004@172.130.13.31:61002;ob>
    public String getContact()
    {
        return "\""+ext+"\"<sip:"+ext+"@"+localIp+":"+localPort+";ob>";
    }
    //sip:77001@172.168.200.78:5060

    public String getSipExtIpPort()
    {
        return "sip:"+ext+"@"+getSipIpPort();
    }
    public String getCseq()
    {
        return String.valueOf(cseq);
    }


    public long getSsrc() {
        return ssrc;
    }

    public void setSsrc(long ssrc) {
        this.ssrc = ssrc;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }



    public int getRtpSeq() {
        if(rtpSeq==0)
        {
            rtpSeq= IdUtils.generateSeq(callId);
        }
        return rtpSeq++;
    }

    public void setRtpSeq(int rtpSeq) {
        this.rtpSeq = rtpSeq;
    }

    public void initTimeStamp()
    {
        sendRRTime=System.currentTimeMillis();
    }

    public int getTimeStamp()
    {
        return (int)((System.currentTimeMillis()-sendRRTime)/20+1)*PCMA_STEP_SIZE;
    }


    public InetSocketAddress mySideRtpSender()
    {
        if(sideRtpSender==null)
        {
            sideRtpSender=new InetSocketAddress(getSideRtcpIp(), getSideRtpPort());
        }
       return sideRtpSender;
    }



    public void addSipMsg(String msg)
    {
        sipList.add(msg);
    }






}
